package com.platform.modules.chat.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.platform.common.config.PlatformConfig;
import com.platform.common.constant.AppConstants;
import com.platform.common.constant.HeadConstant;
import com.platform.common.enums.GenderEnum;
import com.platform.common.enums.YesOrNoEnum;
import com.platform.common.exception.BaseException;
import com.platform.common.exception.LoginException;
import com.platform.common.shiro.ShiroUtils;
import com.platform.common.shiro.utils.Md5Utils;
import com.platform.common.utils.DateUtils;
import com.platform.common.utils.ServletUtils;
import com.platform.common.utils.redis.GeoHashUtils;
import com.platform.common.utils.redis.RedisUtils;
import com.platform.common.web.service.impl.BaseServiceImpl;
import com.platform.modules.auth.service.TokenService;
import com.platform.modules.auth.vo.AuthVo01;
import com.platform.modules.chat.dao.ChatUserDao;
import com.platform.modules.chat.domain.ChatMsgTemplate;
import com.platform.modules.chat.domain.ChatPay;
import com.platform.modules.chat.domain.ChatUser;
import com.platform.modules.chat.service.ChatMsgTemplateService;
import com.platform.modules.chat.service.ChatPayService;
import com.platform.modules.chat.service.ChatUserService;
import com.platform.modules.chat.utils.UUIDgen;
import com.platform.modules.chat.vo.MyVo09;
import com.platform.modules.push.service.ChatPushService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户表 服务层实现
 * </p>
 */
@Slf4j
@Service("chatUserService")
public class ChatUserServiceImpl extends BaseServiceImpl<ChatUser> implements ChatUserService {

    @Resource
    private ChatUserDao chatUserDao;

    @Resource
    private TokenService tokenService;

    @Resource
    private ChatPushService chatPushService;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private GeoHashUtils geoHashUtils;

    @Autowired
    private ChatPayService chatPayService;

    @Autowired
    private ChatMsgTemplateService chatMsgTemplateService;

    @Autowired
    public void setBaseDao() {
        super.setBaseDao(chatUserDao);
    }

    @Override
    public List<ChatUser> queryList(ChatUser t) {
        List<ChatUser> dataList = chatUserDao.queryList(t);
        return dataList;
    }

    @Override
    public void register(AuthVo01 authVo) {
        String phone = authVo.getPhone();
        String password = authVo.getPassword();
        String nickName = authVo.getNickName();
        String msg = "此手机号码已注册过，请勿重复注册";
        // 验证手机号是否注册过
        if (this.queryCount(new ChatUser().setPhone(phone)) > 0) {
            throw new BaseException(msg);
        }
        String salt = RandomUtil.randomString(4);
        String chatNo = IdUtil.simpleUUID();
        int initnum = 6;
        if (null!=authVo.getInviteCode()&&!"".equals(authVo.getInviteCode())) {
            initnum = initnum+6;
            ChatUser chatUser = this.queryByInviteCode(authVo.getInviteCode());
            ChatUser chatUserU = new ChatUser()
                    .setUserId(chatUser.getUserId())
                    .setInitnum(chatUser.getInitnum()+6);
            this.updateById(chatUserU);
        }
        ChatUser cu = new ChatUser()
                .setNickName(nickName)
                .setChatNo(chatNo)
                .setGender(GenderEnum.MALE)
                .setPortrait(AppConstants.DEFAULT_PORTRAIT)
                .setSalt(salt)
                .setPhone(phone)
                .setPassword(Md5Utils.credentials(password, salt))
                .setStatus(YesOrNoEnum.YES)
                .setCreateTime(DateUtil.date())
                .setInitnum(initnum)
                .setInviteCode(UUIDgen.getUUID(10));
        if (null!=authVo.getInviteCode()&&!"".equals(authVo.getInviteCode())) {
            cu.setVersion(authVo.getInviteCode());
        }
        try {
            this.add(cu);
        } catch (org.springframework.dao.DuplicateKeyException e) {
            throw new BaseException(msg);
        }
    }

    @Override
    public ChatUser queryByPhone(String phone) {
        return this.queryOne(new ChatUser().setPhone(phone));
    }

    @Override
    public ChatUser queryByInviteCode(String inviteCode) {
        return this.queryOne(new ChatUser().setInviteCode(inviteCode));
    }

    @Override
    public ChatUser queryByOpenId(String openId) {
        return this.queryOne(new ChatUser().setOpenId(openId));
    }

    @Override
    public void resetPass(Long userId, String password) {
        String salt = RandomUtil.randomString(4);
        ChatUser chatUser = new ChatUser()
                .setUserId(userId)
                .setSalt(salt)
                .setPassword(Md5Utils.credentials(password, salt));
        this.updateById(chatUser);
    }

    @Override
    public void updateOpenId(String openId, Long userId) {
        chatUserDao.updateUserOpenId(openId, userId);
    }

    @Override
    public void editPass(String password, String pwd) {
        // 当前用户
        ChatUser cu = getById(ShiroUtils.getUserId());
        if (!Md5Utils.credentials(password, cu.getSalt()).equalsIgnoreCase(cu.getPassword())) {
            throw new BaseException("旧密码不正确");
        }
        String salt = RandomUtil.randomString(4);
        ChatUser chatUser = new ChatUser()
                .setUserId(cu.getUserId())
                .setSalt(salt)
                .setPassword(Md5Utils.credentials(pwd, salt));
        this.updateById(chatUser);
    }

    @Override
    public void editChatNo(String chatNo) {
        Long userId = ShiroUtils.getUserId();
        String errMsg = "小助手号已被占用，请重新输入";
        // 校验
        ChatUser cu = this.queryOne(new ChatUser().setChatNo(chatNo));
        if (cu != null && !userId.equals(cu.getUserId())) {
            throw new BaseException(errMsg);
        }
        try {
            // 更新
            this.updateById(new ChatUser().setUserId(userId).setChatNo(chatNo));
        } catch (org.springframework.dao.DuplicateKeyException e) {
            throw new BaseException(errMsg);
        }
    }

    @Override
    public MyVo09 getInfo() {
        // 当前用户
        String jsonStr = redisUtils.get(ShiroUtils.getUserId()+"");
        if (null!=jsonStr&&!"".equals(jsonStr)) {
            JSONObject json = (JSONObject)JSONObject.toJSON(JSON.parse(jsonStr));
            MyVo09 rtn = JSONObject.toJavaObject(json, MyVo09.class);
            return rtn;
        }else {
            ChatUser cu = findById(ShiroUtils.getUserId());
            MyVo09 rtn = BeanUtil.toBean(cu, MyVo09.class).setPhone(DesensitizedUtil.mobilePhone(cu.getPhone()));
            String urlCode = "http://ocr.shudingkj.com?invite_code="+cu.getInviteCode();
            int width = 300;
            int height = 300;
            QrConfig qrConfig = QrConfig.create().setWidth(width).setHeight(height);
            byte[] data = QrCodeUtil.generatePng(urlCode, qrConfig);
            String result = AppConstants.BASE64_PREFIX.concat(Base64.encode(data));
            rtn.setInviteQr(result);
            rtn.setUrlCode(urlCode);
            ChatPay chatPayParam = new ChatPay();
            chatPayParam.setUserId(cu.getUserId());
            chatPayParam.setStatus(1);
            List<ChatPay> payList = chatPayService.queryList(chatPayParam);
            if (null!=payList&&payList.size()>0) {
                for (ChatPay pay:payList) {
                    if (null==rtn.getStartDate()) {
                        rtn.setStartDate(pay.getStartDate());
                    }else if (rtn.getStartDate().after(pay.getStartDate())) {
                        rtn.setStartDate(pay.getStartDate());
                    }
                    if (null==rtn.getEndDate()) {
                        rtn.setEndDate(pay.getEndDate());
                    }else if (rtn.getEndDate().before(pay.getEndDate())) {
                        rtn.setEndDate(pay.getEndDate());
                    }
                }
                if (null!=rtn.getEndDate()) {
                    SimpleDateFormat dFormat = new SimpleDateFormat("yyyy-MM-dd");
                    rtn.setStartDateStr(dFormat.format(rtn.getStartDate()));
                    rtn.setEndDateStr(dFormat.format(rtn.getEndDate()));
                }
            }
            if (rtn.getPortrait().indexOf("http")<0) {
                rtn.setPortrait(AppConstants.DEFAULT_URL+rtn.getPortrait());
            }
            List<ChatMsgTemplate> msgList = null;
            Map<Object, Object> msgValue = redisUtils.hGetAll("msglist");
            if (null == msgValue.get("ALL")||msgValue.size()<=0) {
                msgList = chatMsgTemplateService.queryList(new ChatMsgTemplate());
                Map<String, String> msgMap = new HashMap<>();
                msgMap.put("ALL", JSON.toJSONString(msgList));
                redisUtils.hPutAll("msglist", msgMap);
            }else {
                msgList = JSON.parseArray(msgValue.get("ALL").toString(), ChatMsgTemplate.class);
            }
            rtn.setMsgList(msgList);
            redisUtils.set(cu.getUserId()+"", JSON.toJSONString(rtn), AppConstants.REDIS_QR_CODE_TIME, TimeUnit.MINUTES);
            return rtn;
        }
    }

    @Override
    public String getQrCode() {
        String key = AppConstants.REDIS_QR_CODE + ShiroUtils.getUserId();
        if (redisUtils.hasKey(key)) {
            return redisUtils.get(key);
        }
        return resetQrCode();
    }

    @Override
    public String resetQrCode() {
        Long userId = ShiroUtils.getUserId();
        ChatUser chatUser = getById(userId);
        String content = AppConstants.QR_CODE_USER + userId;
        File original = HttpUtil.downloadFileFromUrl(chatUser.getPortrait() + AppConstants.IMAGE_PARAM, FileUtil.file(PlatformConfig.ROOT_PATH));
        QrConfig qrConfig = QrConfig.create().setWidth(AppConstants.QR_CODE_SIZE).setHeight(AppConstants.QR_CODE_SIZE).setImg(original);
        byte[] data = QrCodeUtil.generatePng(content, qrConfig);
        // 删除临时图片
        FileUtil.del(original);
        String value = AppConstants.BASE64_PREFIX.concat(Base64.encode(data));
        redisUtils.set(AppConstants.REDIS_QR_CODE + userId, value, AppConstants.REDIS_QR_CODE_TIME, TimeUnit.DAYS);
        return value;
    }

    @Transactional
    @Override
    public void deleted() {
        // 移除缓存
        removeCache();
        // 更新用户
        ChatUser cu = new ChatUser()
                .setUserId(ShiroUtils.getUserId())
                .setDeletedTime(DateUtil.date());
        this.updateById(cu);
    }

    @Transactional
    @Override
    public Dict doLogin(AuthenticationToken authenticationToken, String cid) {
        String msg = null;
        try {
            ShiroUtils.getSubject().login(authenticationToken);
        } catch (LoginException e) {
            msg = e.getMessage();
        } catch (AuthenticationException e) {
            msg = "手机号或密码不正确";
        } catch (Exception e) {
            msg = "未知异常";
            log.error(msg, e);
        }
        if (!StringUtils.isEmpty(msg)) {
            throw new BaseException(msg);
        }
        Long userId = ShiroUtils.getUserId();
        ChatUser oldUserInfo = this.getById(userId);
        String token = oldUserInfo.getToken();
        Date oldLoginTime = oldUserInfo.getLoginTime();
        if (!StringUtils.isEmpty(token)) {
            tokenService.deleteToken(token);
        }
        // 生成新TOKEN
        token = tokenService.generateToken();
        Date loginTime = DateUtils.getNowDate();
        ChatUser chatUser = new ChatUser()
                .setUserId(userId)
                .setToken(token)
                .setLoginTime(loginTime);
        if (StringUtils.isEmpty(oldUserInfo.getInviteCode())) {
            chatUser.setInviteCode(UUIDgen.getUUID(10));
        }
        ChatPay chatPayParam = new ChatPay();
        chatPayParam.setUserId(userId);
        chatPayParam.setStatus(1);
        List<ChatPay> payList = chatPayService.queryList(chatPayParam);
        if (null!=payList&&payList.size()>0) {
            SimpleDateFormat dFormat = new SimpleDateFormat("yyyyMMdd");
            String oldLoginDStr = dFormat.format(oldLoginTime);
            String loginDStr = dFormat.format(loginTime);
            ChatPay pay = payList.get(0);
            if (loginTime.before(pay.getEndDate())&&!loginDStr.equals(oldLoginDStr)) {
                chatUser.setInitnum(100);
            }
            for (ChatPay payItem:payList) {
                if (payItem.getEndDate().before(DateUtils.getNowDate())) {
                    payItem.setStatus(0);
                    chatPayService.updateById(pay);
                }
            }
        }
        if (!StringUtils.isEmpty(cid)) {
            // 注册别名
            chatPushService.setAlias(userId, cid);
            // 更新cid
            chatUser.setCid(cid);
        }
        // 更新token
        this.updateById(chatUser);
        // 拉取离线消息
        String device = ServletUtils.getRequest().getHeader(HeadConstant.DEVICE);
        if (HeadConstant.DEVICE_IOS.contains(device) || HeadConstant.DEVICE_ANDROID.contains(device)) {
            chatPushService.pullOffLine(userId);
        }
        redisUtils.delete(userId+"");
        return Dict.create().set("token", token).set("userid", userId).set("openid", oldUserInfo.getOpenId());
    }

    @Override
    public void logout() {
        try {
            // 移除缓存
            removeCache();
            // 执行退出
            ShiroUtils.getSubject().logout();
            log.info("退出成功。。。。");
        } catch (Exception ex) {
            log.error("退出异常", ex);
        }
    }

    @Override
    public void bindCid(String cid) {
        Long userId = ShiroUtils.getUserId();
        ChatUser chatUser = getById(userId);
        if (!cid.equalsIgnoreCase(chatUser.getCid())) {
            // 更新cid
            this.updateById(new ChatUser().setCid(cid).setUserId(userId));
            // 注册别名
            chatPushService.setAlias(userId, cid);
        }
        // 拉取离线消息
        chatPushService.pullOffLine(userId);
    }

    /**
     * 移除缓存
     */
    private void removeCache() {
        Long userId = ShiroUtils.getUserId();
        ChatUser chatUser = this.getById(userId);
        if (chatUser != null) {
            // 清理token
            String token = chatUser.getToken();
            if (!StringUtils.isEmpty(token)) {
                tokenService.deleteToken(token);
            }
            // 清理cid
            String cid = chatUser.getCid();
            if (!StringUtils.isEmpty(cid)) {
                chatPushService.delAlias(userId, cid);
            }
        }
        String userStr = NumberUtil.toStr(userId);
        // 附近的人
        geoHashUtils.remove(AppConstants.REDIS_NEAR, userStr);
        // 摇一摇
        redisUtils.lRemove(AppConstants.REDIS_SHAKE, 0, userStr);
        redisUtils.delete(userId+"");
    }

}
